Netty ChannelOption参数详解

netty.png

文章摘要

在使用 Netty 进行客户端和服务端开发时,需要对 serverBootStrap 的 option 和 childOption 进行自定义的一些配置,具体的 Channel 配置参数可以查看 ChannelOption 里面声明的常量,按使用场景可以划分为 Netty 通用参数、Socket 套接字参数以及 IP 参数。

源码

以下代码就是在实际项目中一个 Netty server 的具体 ChannelOption 参数配置:

1
2
3
4
5
serverBootstrap = serverBootstrap.childHandler(new ListInitializer())   
.option(ChannelOption.SO_REUSEADDR, true)
.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(ServerInfo.getInstance().getLowWaterMark(), ServerInfo.getInstance().getHighWaterMark()))
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);

ChannelOption 源码中定义的参数如下,当前使用的版本为 netty-all-4.1.29.Final:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public static final ChannelOption<ByteBufAllocator> ALLOCATOR = valueOf("ALLOCATOR");
public static final ChannelOption<RecvByteBufAllocator> RCVBUF_ALLOCATOR = valueOf("RCVBUF_ALLOCATOR");
public static final ChannelOption<MessageSizeEstimator> MESSAGE_SIZE_ESTIMATOR = valueOf("MESSAGE_SIZE_ESTIMATOR");

public static final ChannelOption<Integer> CONNECT_TIMEOUT_MILLIS = valueOf("CONNECT_TIMEOUT_MILLIS");
/**
* @deprecated Use {@link MaxMessagesRecvByteBufAllocator}
*/
@Deprecated
public static final ChannelOption<Integer> MAX_MESSAGES_PER_READ = valueOf("MAX_MESSAGES_PER_READ");
public static final ChannelOption<Integer> WRITE_SPIN_COUNT = valueOf("WRITE_SPIN_COUNT");
/**
* @deprecated Use {@link #WRITE_BUFFER_WATER_MARK}
*/
@Deprecated
public static final ChannelOption<Integer> WRITE_BUFFER_HIGH_WATER_MARK = valueOf("WRITE_BUFFER_HIGH_WATER_MARK");
/**
* @deprecated Use {@link #WRITE_BUFFER_WATER_MARK}
*/
@Deprecated
public static final ChannelOption<Integer> WRITE_BUFFER_LOW_WATER_MARK = valueOf("WRITE_BUFFER_LOW_WATER_MARK");
public static final ChannelOption<WriteBufferWaterMark> WRITE_BUFFER_WATER_MARK =
valueOf("WRITE_BUFFER_WATER_MARK");

public static final ChannelOption<Boolean> ALLOW_HALF_CLOSURE = valueOf("ALLOW_HALF_CLOSURE");
public static final ChannelOption<Boolean> AUTO_READ = valueOf("AUTO_READ");

/**
* If {@code true} then the {@link Channel} is closed automatically and immediately on write failure.
* The default value is {@code true}.
*/
public static final ChannelOption<Boolean> AUTO_CLOSE = valueOf("AUTO_CLOSE");

public static final ChannelOption<Boolean> SO_BROADCAST = valueOf("SO_BROADCAST");
public static final ChannelOption<Boolean> SO_KEEPALIVE = valueOf("SO_KEEPALIVE");
public static final ChannelOption<Integer> SO_SNDBUF = valueOf("SO_SNDBUF");
public static final ChannelOption<Integer> SO_RCVBUF = valueOf("SO_RCVBUF");
public static final ChannelOption<Boolean> SO_REUSEADDR = valueOf("SO_REUSEADDR");
public static final ChannelOption<Integer> SO_LINGER = valueOf("SO_LINGER");
public static final ChannelOption<Integer> SO_BACKLOG = valueOf("SO_BACKLOG");
public static final ChannelOption<Integer> SO_TIMEOUT = valueOf("SO_TIMEOUT");

public static final ChannelOption<Integer> IP_TOS = valueOf("IP_TOS");
public static final ChannelOption<InetAddress> IP_MULTICAST_ADDR = valueOf("IP_MULTICAST_ADDR");
public static final ChannelOption<NetworkInterface> IP_MULTICAST_IF = valueOf("IP_MULTICAST_IF");
public static final ChannelOption<Integer> IP_MULTICAST_TTL = valueOf("IP_MULTICAST_TTL");
public static final ChannelOption<Boolean> IP_MULTICAST_LOOP_DISABLED = valueOf("IP_MULTICAST_LOOP_DISABLED");

public static final ChannelOption<Boolean> TCP_NODELAY = valueOf("TCP_NODELAY");

@Deprecated
public static final ChannelOption<Boolean> DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION =
valueOf("DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION");

public static final ChannelOption<Boolean> SINGLE_EVENTEXECUTOR_PER_GROUP =
valueOf("SINGLE_EVENTEXECUTOR_PER_GROUP");

源码中可以看到,参数MAX_MESSAGES_PER_READWRITE_BUFFER_HIGH_WATER_MARKWRITE_BUFFER_LOW_WATER_MARKDATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION 已经弃用,下面简单的整理一下 ChannelOption 中参数的使用方法。

使用

Netty 通用参数

  • ChannelOption.ALLOCATOR

ByteBuf 的分配器,默认值为 ByteBufAllocator.DEFAULT,在 4.0 版本中,默认的 allocator 为 UnpooledByteBufAllocator,4.1版本中增强了缓冲区泄漏追踪机制,默认的 allocator 就改为了 PooledByteBufAllocator。即 4.1 版本使用对象池,实现了重用缓冲区(可以直接只用这个配置)。

1
2
bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
bootstrap.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
  • ChannelOption.RCVBUF_ALLOCATOR

用于 Channel 分配接受 Buffer 的分配器,默认值为 AdaptiveRecvByteBufAllocator.DEFAULT,是一个自适应的接受缓冲区分配器,能根据接受到的数据自动调节大小。可选值为FixedRecvByteBufAllocator,固定大小的接受缓冲区分配器。

  • ChannelOption.MESSAGE_SIZE_ESTIMATOR

消息大小估算器,默认为 DefaultMessageSizeEstimator.DEFAULT。估算 ByteBuf、ByteBufHolder 和 FileRegion 的大小,其中 ByteBuf 和 ByteBufHolder 为实际大小,FileRegion 估算值为0。该值估算的字节数在计算水位时使用,FileRegion 为0可知FileRegion 不影响高低水位。

  • ChannelOption.CONNECT_TIMEOUT_MILLIS

连接超时毫秒数,默认值 30000 毫秒。

  • ChannelOption.MAX_MESSAGES_PER_READ

【弃用,使用 MaxMessagesRecvByteBufAllocator】一次 Loop 读取的最大消息数,对于 ServerChannel 或者 NioByteChannel,默认值为 16,其他 Channel 默认值为1。默认值这样设置,是因为:ServerChannel 需要接受足够多的连接,保证大吞吐量,NioByteChannel 可以减少不必要的系统调用 select。

  • ChannelOption.WRITE_SPIN_COUNT

一个 Loop 写操作执行的最大次数,默认值为 16。也就是说,对于大数据量的写操作至多进行 16 次,如果 16 次仍没有全部写完数据,此时会提交一个新的写任务给 EventLoop,任务将在下次调度继续执行。这样,其他的写请求才能被响应不会因为单个大数据量写请求而耽误。

  • ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK

【弃用,使用 #WRITE_BUFFER_WATER_MARK】写高水位标记,默认值 64 KB。如果 Netty 的写缓冲区中的字节超过该值,Channel 的 isWritable() 返回 False。

  • ChannelOption.WRITE_BUFFER_LOW_WATER_MARK

【弃用,使用 #WRITE_BUFFER_WATER_MARK】写低水位标记,默认值 32 KB。当 Netty 的写缓冲区中的字节超过高水位之后若下降到低水位,则 Channe 的 isWritable() 返回 True。写高低水位标记使用户可以控制写入数据速度,从而实现流量控制。推荐做法是:每次调用 channl.write(msg) 方法首先调用 channel.isWritable() 判断是否可写。

  • ChannelOption.WRITE_BUFFER_WATER_MARK

写水位标记,使用方法见 WRITE_BUFFER_HIGH_WATER_MARKWRITE_BUFFER_LOW_WATER_MARK

  • ChannelOption.ALLOW_HALF_CLOSURE

一个连接的远端关闭时本地端是否关闭,默认值为 False。值为 False 时,连接自动关闭;为 True 时,触发 ChannelInboundHandler 的 userEventTriggered() 方法,事件为 ChannelInputShutdownEvent。

  • ChannelOption.AUTO_READ

自动读取,默认值为 True。Netty 只在必要的时候才设置关心相应的 I/O 事件。对于读操作,需要调用 channel.read() 设置关心的 I/O 事件为 OP_READ,这样若有数据到达才能读取以供用户处理。该值为 True 时,每次读操作完毕后会自动调用 channel.read(),从而有数据到达便能读取;否则,需要用户手动调用 channel.read()。需要注意的是:当调用 config.setAutoRead(boolean) 方法时,如果状态由 false 变为 true,将会调用 channel.read() 方法读取数据;由 true 变为 false,将调用 config.autoReadCleared() 方法终止数据读取。

  • ChannelOption.AUTO_CLOSE

自动关闭,默认值为 True。如果 True,则 Channel 在写入失败时立即自动关闭。

  • ChannelOption.DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION

【弃用】DatagramChannel 注册的 EventLoop 即表示已激活。

  • ChannelOption.SINGLE_EVENTEXECUTOR_PER_GROUP

单线程执行 ChannelPipeline 中的事件,默认值为 True。该值控制执行 ChannelPipeline 中执行 ChannelHandler 的线程。如果为 True,整个 pipeline 由一个线程执行,这样不需要进行线程切换以及线程同步,是 Netty4 的推荐做法;如果为 False,ChannelHandler 中的处理过程会由 Group 中的不同线程执行。

Socket 套接字参数

  • ChannelOption.SO_BROADCAST

对应套接字选项中的 SO_BROADCAST,设置广播模式。允许或禁止发送广播数据,只有数据报套接字支持广播,并且是在支持广播消息的网络上才能使用。

  • ChannelOption.SO_KEEPALIVE

对应套接字选项中的 SO_KEEPALIVE,是否启动心跳包活机制,默认值为 False。该参数用于设置 TCP 连接,当设置该选项以后,TCP 会主动测试这个连接的状态,如果该连接长时间没有数据交流,TCP 会自动发送一个活动探测数据报文(keep-alive probe),来检测连接是否存活。可以将此功能视为TCP的心跳机制,需要注意的是:默认的心跳间隔是 7200s 即 2 小时。

  • ChannelOption.SO_SNDBUF

对应套接字选项中的 SO_SNDBUF,TCP数据接收缓冲区大小。接收缓冲区即 TCP 接收滑动窗口,用于保存网络协议站内收到的数据,直到应用程序读取成功。Linux操作系统可使用命令: cat /proc/sys/net/ipv4/tcp_rmem 查询其大小。一般情况下,该值可由用户在任意时刻设置,但当设置值超过 64KB 时,需要在连接到远端之前设置。

联系 SO_RCVBUF 参数来看,每个套接字都有一个发送缓冲区和一个接收缓冲区,TCP 设置这个两个选项注意顺序:对于客户端必须在调用 connect 之前,对于服务器端应该在调用 listen 之前,因为窗口选项是在建立连接时用 syn 分节与对端互换得到的。

TCP 套接字的缓冲区大小至少应该是 MSS 的4倍;MSS=MTU-40 头部,一般以太网卡 MTU 是1500;典型缓冲区默认大小是 8192 字节或者更大。对于一次发送大量数据,可以增加到48K,64K等,为了达到最佳性能,缓冲区可能至少要与BDP(带宽延迟乘积)一样大小。在实际的过程中发送数据和接收数据量比较大,提高接收缓冲区能够减少发送端的阻塞,避免 send()、recv() 不断的循环收发。

  • ChannelOption.SO_RCVBUF

对应于套接字选项中的SO_RCVBUF,TCP数据发送缓冲区大小。发送缓冲区即 TCP 发送滑动窗口,用于保存发送数据直到发送成功。Linux 操作系统可使用命令:cat /proc/sys/net/ipv4/tcp_smem 查询其大小。

  • ChannelOption.SO_REUSEADDR

对应套接字选项中的 SO_REUSEADDR,这个参数表示允许重复使用本地地址和端口,默认值False。例如,某个服务占用了 TCP 的 8080 端口,其他服务再对这个端口进行监听就会报错, SO_REUSEADDR 这个参数就是用来解决这个问题的,该参数允许服务公用一个端口,这个在服务器程序中比较常用,例如某个进程非正常退出,对一个端口的占用可能不会立即释放,这时候如果不设置这个参数,其他进程就不能立即使用这个端口。而设置 SO_REUSEADDR,就允许在同一个端口上启动同一服务器的多个实例,只要每个实例绑定不同的本地地址即可。

  • ChannelOption.SO_LINGER

对应套接字选项中的 SO_LINGER,关闭Socket的延迟时间,默认值为-1,表示禁用该功能。Linux内核默认的处理方式是当用户调用 close() 方法的时候,函数返回,在可能的情况下,尽量发送数据,不一定保证会发生剩余的数据,造成了数据的不确定性,使用 SO_LINGER 可以阻塞 close() 的调用时间,直到数据完全发送。

具体配置方式:-1 表示 socket.close() 方法立即返回,但 OS 底层会将发送缓冲区全部发送到对端。0 表示 socket.close() 方法立即返回,OS 放弃发送缓冲区的数据直接向对端发送 RST 包,对端收到复位错误。非 0 整数值表示调用 socket.close() 方法的线程被阻塞直到延迟时间到或发送缓冲区中的数据发送完毕,若超时,则对端会收到复位错误。

  • ChannelOption.SO_BACKLOG

对应的是 TCP/IP 协议 listen 函数中的 backlog 参数,函数 listen(int socketfd,int backlog) 用来初始化服务端可连接队列。服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接,多个客户端来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理,backlog 参数指定了用于临时存放已完成三次握手的请求队列的大小。如果未设置或者设置值为小于 1,则 Java 将会使用默认值50。

  • ChannelOption.SO_TIMEOUT

用于设置接受数据的等待的超时时间,单位为毫秒,默认值为 0,表示无限等待。

  • ChannelOption.TCP_NODELAY

对应于套接字选项中的 TCP_NODELAY,该参数的使用与 Nagle 算法有关,默认情况其是启用的。

在 TCP/IP 协议中,无论发送多少数据,总是需要在数据前面加上协议头,同时,对方接收到数据,也需要发送 ACK 表示确认。为了尽可能地利用网络宽带,TCP 总是希望尽可能的发送足够大的数据。Nagle 算法的目的就是为了尽可能的发送大块数据,避免网络中充斥这小块数据。虽然该方式有效提高网络的有效负载,但是却造成了延时。

TCP_NODELAY 就是用于关闭 Nagle 算法。如果要求高实时性,有数据发送时就马上发送,就将该选项设置为 true 关闭 Nagle 算法;如果要减少发送次数减少网络交互,就设置为 false 启用 Nagle 算法,等累积一定大小后再发送。

IP 参数

  • ChannelOption.IP_TOS

IP 参数,设置 IP 头部的 Type-of-Service 字段,用于描述 IP 包的优先级和 QoS 选项。

  • ChannelOption.IP_MULTICAST_ADDR

对应 IP 参数 IP_MULTICAST_IF,设置对应地址的网卡为多播模式。

  • ChannelOption.IP_MULTICAST_IF

对应 IP 参数 IP_MULTICAST_IF2,同上但支持 IPV6。

  • ChannelOption.IP_MULTICAST_TTL

IP 参数,多播数据报的 time-to-live 即存活跳数。

  • ChannelOption.IP_MULTICAST_LOOP_DISABLED

对应 IP 参数 IP_MULTICAST_LOOP,设置本地回环接口的多播功能。由于 IP_MULTICAST_LOOP返回 True 表示关闭,所以 Netty 加上后缀 _DISABLED 防止歧义。

附:

参考文献

https://www.cnblogs.com/googlemeoften/p/6082785.html

https://www.jianshu.com/p/0bff7c020af2